package org.moon.framework.autoconfigure.datasource.aop;

import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.moon.framework.autoconfigure.config.SpringContextConfig;
import org.moon.framework.autoconfigure.datasource.annotation.DS;
import org.moon.framework.autoconfigure.datasource.domain.MoonDataSource;
import org.moon.framework.autoconfigure.datasource.routing.DynamicDataSourceContextHolder;
import org.moon.framework.autoconfigure.exception.domain.MoonException;
import org.moon.framework.autoconfigure.mybatisplus.props.JdbcProperties;
import org.moon.framework.autoconfigure.utils.Func;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 多数据源处理
 * @author moon
 */
@Aspect
@Slf4j
public class DataSourceAspect {

    public static Map<Object, Object> dataSourcesMap = new ConcurrentHashMap<>(10);

    @Pointcut("@annotation(org.moon.framework.autoconfigure.datasource.annotation.DS)"
            + "|| @within(org.moon.framework.autoconfigure.datasource.annotation.DS)")
    public void dsPointCut() {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DS ds = getDataSource(point);
        if (ds==null) {
            return point.proceed();
        }
        changeDataSourceByName(ds.value());
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 获取需要切换的数据源
     */
    private DS getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DS dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DS.class);
        if (Objects.nonNull(dataSource)) {
            return dataSource;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DS.class);
    }

    /**
     * 根据名称初始化数据源
     * 首先从缓存里取，如果取不到则重新构建一个
     * TODO 此处有问题，最好是在启动时一次将所有的数据源都初始化了
     */
    public static void changeDataSourceByName(String name){
        HikariDataSource hikariDataSource = (HikariDataSource)dataSourcesMap.get(name);
        if(hikariDataSource!=null){
            DynamicDataSourceContextHolder.setDataSourceType(name,dataSourcesMap);
            return;
        }
        String sql = "select driver_class as driverClass,url as jdbcUrl, user_name as userName,password from sys_datasource where is_delete=0 and name=?";
        List<MoonDataSource> tdsList = SpringContextConfig.getBean(JdbcTemplate.class).query(sql,new Object[]{name},new BeanPropertyRowMapper<>(MoonDataSource.class));
        MoonDataSource dataSource = DataAccessUtils.uniqueResult(tdsList);
        if(dataSource==null){
            throw new MoonException("数据源name="+name+"不存在,请到数据源管理模块配置");
        }
        JdbcProperties jdbcProperties = new JdbcProperties();
        jdbcProperties.setUsername(dataSource.getUserName());
        jdbcProperties.setPassword(dataSource.getPassword());
        jdbcProperties.setUrl(dataSource.getJdbcUrl());
        jdbcProperties.setDriverClassName(dataSource.getDriverClass());
        hikariDataSource = DynamicDataSourceContextHolder.buildDataSource(jdbcProperties);
        dataSourcesMap.put(name, hikariDataSource );
        DynamicDataSourceContextHolder.setDataSourceType(name,dataSourcesMap);
    }
}